+function ($) {
    'use strict';
    var cachedWidth = null;
    var sprintf = function (str) {
        var args = arguments,
            flag = true,
            i = 1;

        str = str.replace(/%s/g, function () {
            var arg = args[i++];

            if (typeof arg === 'undefined') {
                flag = false;
                return '';
            }
            return arg;
        });
        return flag ? str : '';
    };
    var setFieldIndex = function (columns) {
        var i, j, k,
            totalCol = 0,
            flag = [];

        for (i = 0; i < columns[0].length; i++) {
            totalCol += columns[0][i].colspan || 1;
        }

        for (i = 0; i < columns.length; i++) {
            flag[i] = [];
            for (j = 0; j < totalCol; j++) {
                flag[i][j] = false;
            }
        }

        for (i = 0; i < columns.length; i++) {
            for (j = 0; j < columns[i].length; j++) {
                var r = columns[i][j],
                    rowspan = r.rowspan || 1,
                    colspan = r.colspan || 1,
                    index = $.inArray(false, flag[i]);

                if (colspan === 1) {
                    r.fieldIndex = index;
                    // when field is undefined, use index instead
                    if (typeof r.field === 'undefined') {
                        r.field = index;
                    }
                }

                for (k = 0; k < rowspan; k++) {
                    flag[i + k][index] = true;
                }
                for (k = 0; k < colspan; k++) {
                    flag[i][index + k] = true;
                }
            }
        }
    };
    var getFieldIndex = function (columns, field) {
        var index = -1;

        $.each(columns, function (i, column) {
            if (column.field === field) {
                index = i;
                return false;
            }
            return true;
        });
        return index;
    };
    var calculateObjectValue = function (self, name, args, defaultValue) {
        var func = name;

        if (typeof name === 'string') {
            // support obj.func1.func2
            var names = name.split('.');

            if (names.length > 1) {
                func = window;
                $.each(names, function (i, f) {
                    func = func[f];
                });
            } else {
                func = window[name];
            }
        }
        if (typeof func === 'object') {
            return func;
        }
        if (typeof func === 'function') {
            return func.apply(self, args || []);
        }
        if (!func && typeof name === 'string' && sprintf.apply(this, [name].concat(args))) {
            return sprintf.apply(this, [name].concat(args));
        }
        return defaultValue;
    };

    var getItemField = function (item, field, escape) {
        var value = item;
        if (typeof field !== 'string' || item.hasOwnProperty(field)) {
            return escape ? escapeHTML(item[field]) : item[field];
        }
        var props = field.split('.');
        for (var p in props) {
            if (props.hasOwnProperty(p)) {
                value = value && value[props[p]];
            }
        }
        return escape ? escapeHTML(value) : value;
    };

    var getScrollBarWidth = function () {
        if (cachedWidth === null) {
            var inner = $('<p/>').addClass('fixed-table-scroll-inner'),
                outer = $('<div/>').addClass('fixed-table-scroll-outer'),
                w1, w2;

            outer.append(inner);
            $('body').append(outer);

            w1 = inner[0].offsetWidth;
            outer.css('overflow', 'scroll');
            w2 = inner[0].offsetWidth;

            if (w1 === w2) {
                w2 = outer[0].clientWidth;
            }

            outer.remove();
            cachedWidth = w1 - w2;
        }
        return cachedWidth;
    };

    var escapeHTML = function (text) {
        if (typeof text === 'string') {
            return text
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#039;')
                .replace(/`/g, '&#x60;');
        }
        return text;
    };

    var json2Array4Tree = function (options, datas, level, pid) {
        if (!datas) return []
        if (!level) level = 0

        var k = options.key, pk = options.parentKey,
            childKey = options.childKey, childLen = options.childLen,
            levelKey = options.level, isParent = options.isParent, r = [], data

        if (datas) {
            datas.sort(function(a, b) {
                return a[options.order] - b[options.order]
            })
        }

        if ($.isArray(datas)) {
            for (var i = 0, l = datas.length; i < l; i++) {
                data = $.extend({}, datas[i])
                data[levelKey] = level
                r.push(data)
                if (data[childKey]) {
                    delete data[childKey]
                    data[isParent] = true
                    data[childLen] = datas[i][childKey].length
                    pid = data[k]
                    r = r.concat(json2Array4Tree(options, datas[i][childKey], data[levelKey] + 1, pid))
                    pid = null
                }
            }
        } else {
            data = $.extend({}, datas)
            data[levelKey] = level
            r.push(data)
            if (data[childKey]) {
                delete data[childKey]
                data[isParent] = true
                data[childLen] = datas[childKey].length
                pid = data[k]
                r = r.concat(this.json2Array4Tree(options, datas[childKey], data[levelKey] + 1))
                pid = null
            }
        }
        return r
    };

    var array2Json4Tree = function(options, datas) {
        var r = [], temp = [], keys = options.keys, key = keys.key, parentKey = keys.parentKey, childKey = keys.childKey

        if (!$.isArray(datas)) return [datas]

        for (var i = 0, l = datas.length; i < l; i++) {
            temp[datas[i][key]] = datas[i]
        }
        for (var i = 0, l = datas.length; i < l; i++) {
            if (datas[i][parentKey] && datas[i][key] !== datas[i][parentKey]) {
                if (!temp[datas[i][parentKey]])
                    temp[datas[i][parentKey]] = []
                if (!temp[datas[i][parentKey]][childKey])
                    temp[datas[i][parentKey]][childKey] = []

                temp[datas[i][parentKey]][childKey].push(datas[i])
            } else {
                r.push(datas[i])
            }
        }
        return r
    };
    var BoltDataGrid = function (element, options) {
        this.$element = $(element)
        this.options = options
        this.init();
    }

    BoltDataGrid.DEFAULTS = {
        columns: [[]],
        data: [],
        totalField: 'totalElements',
        dataField: 'content',
        sidePagination: 'client', // client or
        dataUrl: undefined,
        paging: true,
        pagingAlign: 'center',
        method: 'post',
        cache: true,
        showHeader: true,
        contentType: 'application/json',
        dataType: 'json',
        ajaxOptions: {},
        paginationHAlign: 'right', //right, left
        paginationVAlign: 'bottom', //bottom, top, both
        paginationDetailHAlign: 'left', //right, left
        treeView          : false,
        keys            : {
            gridChild   : 'gridChild',
            gridNumber  : 'gridNumber',
            gridCheckbox: 'gridCheckbox',
            gridEdit    : 'gridEdit',
            gridIndex   : 'gridIndex',
            dropFlag    : 'dropFlag',
            treePTr     : 'datagrid.tree.parentTr',
            treePData   : 'datagrid.tree.parentData',
            childlen    : 'childlen',
            isExpand    : 'isExpand',
            isParent    : 'isparent'
        },
        collapseIcon: "fa fa-plus-square-o",//折叠样式
        expandIcon: "fa fa-minus-square-o",//展开样式
        treeOptions     : {
            keys        : {
                key       : 'id',
                parentKey : 'pid',
                childKey  : 'children',
                childLen  : 'childLen',
                isParent  : 'isParent',
                level     : 'level',
                order     : 'order',
                isExpand  : 'isExpand'
            },
            simpleData  : true,
            expandAll   : false,
            add         : true
        },
        dropOptions     : {
            drop        : false,
            dropUrl     : null,
            paging      : true,
            scope       : 'drop',
            beforeDrag  : null,
            beforeDrop  : null,
            afterDrop   : 'POST'
        },
        queryParams: undefined,
        pageNumber: 1,
        pageSize: 10,
        pageList: [10, 25, 50, 100],
        cellMinWidth:60,
        sortOrder: 'asc',
        sortStable: false,
        customSort: $.noop,
        selectItemName: 'btSelectItem',
        showCheckbox: false,
        checkboxHeader: true,
        formatAllRows: function () {
            return 'All';
        },
        rowStyle: function (row, index) {
            return {};
        },

        rowAttributes: function (row, index) {
            return {};
        },

        footerStyle: function (row, index) {
            return {};
        },

        onAll: function (name, args) {
            return false;
        },
        onClickCell: function (field, value, row, $element) {
            return false;
        },
        onDblClickCell: function (field, value, row, $element) {
            return false;
        },
        onClickRow: function (item, $element) {
            return false;
        },
        onDblClickRow: function (item, $element) {
            return false;
        },
        onSort: function (name, order) {
            return false;
        },
        onCheck: function (row) {
            return false;
        },
        onUncheck: function (row) {
            return false;
        },
        onCheckAll: function (rows) {
            return false;
        },
        onUncheckAll: function (rows) {
            return false;
        },
        onCheckSome: function (rows) {
            return false;
        },
        onUncheckSome: function (rows) {
            return false;
        },
        onLoadSuccess: function (data) {
            return false;
        },
        onLoadError: function (status) {
            return false;
        },
        onColumnSwitch: function (field, checked) {
            return false;
        },
        onPageChange: function (number, size) {
            return false;
        },
        onSearch: function (text) {
            return false;
        },
        onToggle: function (cardView) {
            return false;
        },
        onPreBody: function (data) {
            return false;
        },
        onPostBody: function () {
            return false;
        },
        onPostHeader: function () {
            return false;
        },
        onExpandRow: function (index, row, $detail) {
            return false;
        },
        onCollapseRow: function (index, row) {
            return false;
        },
        onRefreshOptions: function (options) {
            return false;
        },
        onRefresh: function (params) {
            return false;
        },
        onResetView: function () {
            return false;
        }
    }

    BoltDataGrid.EVENTS = {
        'all.bs.table': 'onAll',
        'click-cell.bs.table': 'onClickCell',
        'dbl-click-cell.bs.table': 'onDblClickCell',
        'click-row.bs.table': 'onClickRow',
        'dbl-click-row.bs.table': 'onDblClickRow',
        'sort.bs.table': 'onSort',
        'check.bs.table': 'onCheck',
        'uncheck.bs.table': 'onUncheck',
        'check-all.bs.table': 'onCheckAll',
        'uncheck-all.bs.table': 'onUncheckAll',
        'check-some.bs.table': 'onCheckSome',
        'uncheck-some.bs.table': 'onUncheckSome',
        'load-success.bs.table': 'onLoadSuccess',
        'load-error.bs.table': 'onLoadError',
        'column-switch.bs.table': 'onColumnSwitch',
        'page-change.bs.table': 'onPageChange',
        'search.bs.table': 'onSearch',
        'toggle.bs.table': 'onToggle',
        'pre-body.bs.table': 'onPreBody',
        'post-body.bs.table': 'onPostBody',
        'post-header.bs.table': 'onPostHeader',
        'expand-row.bs.table': 'onExpandRow',
        'collapse-row.bs.table': 'onCollapseRow',
        'refresh-options.bs.table': 'onRefreshOptions',
        'reset-view.bs.table': 'onResetView',
        'refresh.bs.table': 'onRefresh'
    };


    BoltDataGrid.COLUMN_DEFAULTS= {
        radio: false,
        checkbox: false,
        checkboxEnabled: true,
        field: undefined,
        title: undefined,
        'class': undefined,
        align: undefined, // left, right, center
        width: undefined,
        sortable: false,
        order: 'asc', // asc, desc
        visible: true,
        formatter: undefined,
        events: undefined,
        sorter: undefined,
        sortName: undefined,
        cellStyle: undefined,
        escape : false
    };


    BoltDataGrid.prototype.init = function () {
        this.initContainer();
        this.initTable();
        this.initHeader();
        this.initData();
        this.initPagination();
        this.initBody();
        this.initServer();
        this.initHeadEvent();

    };

    BoltDataGrid.prototype.initContainer = function () {
        this.$container = $([
            '<div class="bootstrap-table">',
                '<div class="bootstrap-table-container">',
                    '<div class="bootstrap-table-header"><table></table></div>',
                    '<div class="bootstrap-table-body">',
                      '<div class="bootstrap-table-loading"></div>',
                    '</div>',
                    '<div class="bootstrap-table-footer"><table><tr></tr></table></div>',
                '</div>',
               this.options.pagination?'<div class="bootstrap-table-pagination"></div>':'',
            '</div>'
        ].join(''));
        this.$container.insertAfter(this.$element);

        this.$tableContainer = this.$container.find('.bootstrap-table-container');
        this.$tableHeader = this.$container.find('.bootstrap-table-header');
        this.$tableBody = this.$container.find('.bootstrap-table-body');
        this.$tableFooter = this.$container.find('.bootstrap-table-footer');
        this.$pagination = this.$container.find('.bootstrap-table-pagination');
        this.$tableLoading = this.$container.find('.bootstrap-table-loading');
        this.$toolbar = $(this.options.toolbar);
        this.$tableBody.append(this.$element);
        this.$container.after('<div class="clearfix"></div>');
        this.$element.addClass("table table-striped table-hover");
    },

    BoltDataGrid.prototype.getRight =  function($th) {
        var width = 0, index = $th.data('fieldIndex'), models = this.columns
        for (var i = index; i >= 0; i--) {
            if(models[i].visible){
                width += this.$tableHeader.find('> table > thead > tr > th:eq('+ i +')').outerWidth()
            }
        }
        return width
    },

    BoltDataGrid.prototype.getChildrens = function(tr, childrens, hasChild, maxLevel) {
            var that = this,   index = tr.data('index'),
                row = that.getData()[index] ,
                childLen = row['childLen'], level = row['level'];
            if (maxLevel) maxLevel --
            if (!childrens) childrens = $()
            if (childLen) {
                tr.nextAll('[datagrid-tree-level-'+ (level + 1)+']').slice(0, childLen).each(function() {
                    childrens = childrens.add(this)

                    if (hasChild && $(this).next().hasClass(that.classnames.tr_child))
                        childrens = childrens.add($(this).next())

                    if (maxLevel)
                        childrens = childrens.add(that.getChildrens($(this), childrens, hasChild, maxLevel))
                })
            }
            return childrens
    }

    BoltDataGrid.prototype.getParents = function(tr, parents, onlyParent, hasChild) {
        var that = this,  index = tr.data('index'),
            row = that.getData()[index] , level = row['level'], prev

        if (!parents) parents = $()
        if (level) {
            prev = tr.prevAll('[datagrid-tree-level-'+ (level - 1) +']:first')
            if (prev.length) {
                parents = parents.add(prev)
                if (hasChild && prev.next().hasClass(that.classnames.tr_child))
                    parents = parents.add(prev.next())
                if (!onlyParent)
                    parents = parents.add(that.getParents(prev, parents, hasChild))
            }
        }
        return parents
    }
    BoltDataGrid.prototype.initTableDnDEvent = function () {
        var that   = this,options = that.options,$trs = that.$body.find('> tr');
        if (options.dropOptions.drop) {
            var beforeDrag = options.dropOptions.beforeDrag;
            if (typeof beforeDrag === 'string')
                beforeDrag = beforeDrag.toFunc()

            if (typeof beforeDrag !== 'function')
                beforeDrag = false

            $trs
                .basedrag('destroy')
                .basedrag({
                    exclude: 'input, button, a, .bolt-lookup, .datagrid-tree-switch, .datagrid-tree-add, .datagrid-child-td',
                    drop: $trs,
                    container : that.$tableBody,
                    beforeDrag: beforeDrag,
                    treeData: that.data,
                    drag: function() {
                        that.isDrag = true
                    },
                    stop: function() {
                        that.isDrag = false
                        $(this).find('> td.datagrid-tree-td').trigger('mouseout.bolt.datagrid.tree')
                    }
                })
                .off('dropover.bolt.basedrag')
                .on('dropover.bolt.basedrag',  function(e, x, y, target) {
                    $trs.removeClass('datagrid-hover datagrid-drop-over-after datagrid-drop-over-before')
                        .find('.datagrid-tree-box')
                        .removeClass('datagrid-drop-over-append')

                    if (this === target[0] ) {
                        return false
                    }
                    if (that.options.treeView) {
                        var $childrens = that.getChildrens(target)

                        if ($.inArray(this, $childrens) !== -1)
                            return false
                    }

                    var $this = $(this).addClass('datagrid-hover'), height = $this.outerHeight() / 2, top = $this.offset().top

                    if (that.options.treeView)
                        height = 5

                    if (y < (top + height)) {
                        $this.addClass('datagrid-drop-over-before').prevAll(':visible:first').addClass('datagrid-drop-over-after')
                    } else {
                        if (!that.options.isTree || (that.options.isTree && y > (top + $this.outerHeight() - height)))
                            $this.addClass('datagrid-drop-over-after')
                        else if (that.options.isTree) {
                            $this.find('.datagrid-tree-box').addClass('datagrid-drop-over-append')
                        }
                    }
                })
                .off('dropout.bolt.basedrag').on('dropout.bolt.basedrag', function(e, data) {
                $trs.removeClass('datagrid-hover datagrid-drop-over-after datagrid-drop-over-before')
                $trs.find('div.datagrid-drop-over-append').removeClass('datagrid-drop-over-append')
            })
                .off('drop.bolt.basedrag').on('drop.bolt.basedrag', function(e, target) {
                var $this = $(this), position, beforeDrop = options.dropOptions.beforeDrop, afterDrop = options.dropOptions.afterDrop

                that.afterDrop = false

                if ($this.hasClass('datagrid-drop-over-before'))
                    position = 'top'
                else if ($this.hasClass('datagrid-drop-over-after'))
                    position = 'bottom'
                else if (that.options.isTree && $this.find('.datagrid-tree-box').hasClass('datagrid-drop-over-append'))
                    position = 'append'

                if (position) {
                    if (position != 'append')
                        that.insertTr(target, $this, position)
                    else {
                        var $parents = that.getParents(target, null, true)

                        if ($.inArray(this, $parents) !== -1) {
                            $this.find('.datagrid-tree-box').removeClass('datagrid-drop-over-append')

                            return false
                        }

                        that.appendTr(target, $this)
                    }
                }

                $trs.removeClass('datagrid-drop-over-after datagrid-drop-over-before')
                    .find('.datagrid-tree-box').removeClass('datagrid-drop-over-append')

                if (afterDrop && that.afterDrop) {
                    if (typeof afterDrop === 'function') {
                        afterDrop.apply(that, [target, $this])
                    } else if (options.dropOptions.dropUrl) {
                        var postData = {pageSize:that.paging.pageSize, pageCurrent:that.paging.pageCurrent},
                            type = options.editType, opts = {url:options.dropOptions.dropUrl, type:'POST', reload:false}
                        postData['list'] = []
                        $.each(that.data, function(i, data) {
                            var _data = $.extend({}, data)

                            if (options.dropOptions.scope !== 'drop' || data[options.keys.dropFlag]) {
                                for (var key in options.keys)
                                    delete _data[options.keys[key]]
                            }
                            if (options.dropOptions.scope === 'drop') {
                                if (data[options.keys.dropFlag]) {
                                    postData['list'].push(_data)
                                }
                            } else {
                                postData['list'].push(_data)
                            }
                        })

                        if (postData) {
                            postData = JSON.stringify(options.dropOptions.paging && options.paging ? postData : postData['list'])

                            if (type && type === 'raw') {
                                opts.data = postData
                                opts.contentType = 'application/json'
                            } else {
                                opts.data = {json:postData}
                                type && (opts.type = type)
                            }

                            Bolt.ajax('doajax', opts)
                        }
                    }
                }
            })
        }
    }
    BoltDataGrid.prototype.initHeadEvent = function () {
        var that   = this,columns = that.columns,
            $resizeMark = that.$tableContainer.find('> .resizeProxy')
            if (!$resizeMark.length) {
                $resizeMark = $('<div class="resizeProxy" style="left:0; display:none;"></div>')
                $resizeMark.appendTo(that.$tableContainer)
            }
           this.$header.find('th').each(function(i) {
                var $th = $(this),  $resize = $th.find('.column-resizemark');
                $resize.on('mousedown', function(e) {
                    var ofLeft = that.$tableBody.scrollLeft(),
                        marginLeft = parseInt(that.$tableBody.css('marginLeft') || 0, 10), left, index = $th.data('fieldIndex'),
                        model = columns[index],width = model.width;

                    if (isNaN(marginLeft)) marginLeft = 0
                    left = that.getRight($th) - ofLeft + marginLeft
                    that.isResize = true;
                    $resizeMark
                        .show()
                        .css({
                            left   : left,
                            bottom :30,
                            cursor : 'col-resize'
                        })
                        .basedrag({
                            scop  : true,
                            cellMinW:20,
                            relObj:$resizeMark,
                            move  : 'horizontal',
                            event : e,
                            stop  : function() {
                                var new_left = $resizeMark.position().left,
                                    move     = new_left - left,
                                    newWidth = move + width

                                if (newWidth < 5) newWidth = 20
                                if (model.minWidth && newWidth < model.minWidth) newWidth = model.minWidth
                                model.width = newWidth
                                setTimeout(function() {
                                    that.$tableHeader.find('> table > thead > tr > th:eq('+ index +')').width(newWidth)
                                    that.$tableBody.find('> table > thead > tr > th:eq('+ index +')').width(newWidth)
                                }, 1) //setTimeout for chrome
                                $resizeMark.hide()
                                that.resizeFlag = true
                            }
                        })

                })

            });


        if(this.$toolbar.length) this.initToolEvent(this.$toolbar,null);
    },

    BoltDataGrid.prototype.insertTr = function(sTr, dTr, position) {
           var that = this, point, options = that.options, keys = options.treeOptions.keys, sData, dData, moveIndex,
                sIndex = sTr.data('index'),
                dIndex = dTr.data('index'),
                hasChild = sTr.next().hasClass(".boltDataGrid-child-tr"),
                _start, _end
            var beforeDrop = options.dropOptions.beforeDrop
            var insertTr = function(sTr, dTr, point) {
                if (position === 'bottom') {
                    if (hasChild)
                        dTr = dTr.next()
                    sTr.insertAfter(dTr)
                } else {
                    sTr.insertBefore(dTr)
                }
            }

            sData = that.data[sIndex]
            dData = that.data[dIndex]

            /* beforeDrop */
            if (beforeDrop && typeof beforeDrop === 'string')
                beforeDrop = beforeDrop.toFunc()
            if (beforeDrop && typeof beforeDrop === 'function') {
                if (!beforeDrop.apply(this, [sData, dData, position])) {
                    return
                }
            }

            if (sIndex > dIndex) {
                point = position === 'top' ? 0 : 1
            } else {
                point = position === 'top' ? -1 : 0
            }

            moveIndex = dIndex + point

            if (moveIndex == sIndex && !options.isTree) return

            if (hasChild)
                sTr = sTr.add(sTr.next())

            if (that.isDom) {
                insertTr(sTr, dTr, point)
            } else {
                sData = that.data[sIndex]
                dData = that.data[dIndex]

                if (options.isTree) {
                    var len, option = {}, append = false,
                        sParent = that.getParents(sTr.first(), null, true),
                        dParent = that.getParents(dTr, null, true)

                    if (dData[keys.level] === sData[keys.level] && dData[keys.order] == sData[keys.order] + (position === 'bottom' ? -1 : 1)) {
                        return
                    }

                    /* to append */
                    // if bottom
                    if (position === 'bottom' && dData[keys.isParent]) {
                        append        = true
                        option._dTr   = dTr
                        option.dIndex = dIndex + 1
                        option.isUp   = true
                    }
                    // if different level or not the same parent
                    else if (sTr.data('level') != dTr.data('level') || sParent[0] !== dParent[0]) {
                        append        = true
                        option._dTr   = dTr
                        option.dIndex = moveIndex
                        dTr           = dParent

                        if (position === 'top')
                            option.isBefore = true

                        if (sIndex < dIndex) {
                            option.dIndex ++
                        }
                    }
                    if (append) {
                        that.appendTr(sTr, dTr, option)

                        return
                    }

                    sTr = that.getChildrens(sTr, sTr, true)
                    len = (hasChild ? sTr.length / 2 : sTr.length)

                    that.data.moveItems(sIndex, moveIndex, len)


                    _start = (sIndex > dIndex ? dIndex : sIndex)
                    _end = (sIndex > dIndex ? sIndex : dIndex) + (len - 1)
                } else {
                    that.data.move(sIndex, moveIndex)

                    _start = sIndex > dIndex ? dIndex : sIndex
                    _end = sIndex > dIndex ? sIndex : dIndex
                }
                insertTr(sTr, dTr, point)
                that.updateIndex()
                // update the seqs
                if (sParent) {
                    var _childs = that.getChildrens(sParent, null, false, 1)

                    _childs.each(function(i) {
                        var _trData = that.data[that.tools.getNoChildDataIndex($(this).index())]

                        if (_trData && (_trData[options.keys.gridIndex] >= _start && _trData[options.keys.gridIndex] <= _end)) {
                            _trData[keys.order] = i
                        }
                    })
                } else {
                    for (var i = _start; i <= _end; i++) {
                        var _trData = that.data[i]

                        _trData[options.keys.dropFlag] = true
                    }
                }

                that.afterDrop = true
            }
     }
     
    BoltDataGrid.prototype.updateIndex=function () {
         var trs = this.$body.find('tr'), row = null;
        for (var i = 0; i < trs.length; i++) {
            row = $(trs[i]);
            row.data('index', i).attr('data-index', i);
        }
    }

    BoltDataGrid.prototype.initTable = function () {
        var that = this,
            columns = [],
            options = that.options;
        this.$header = this.$element.find('>thead');
        if (!this.$header.length) {
            this.$header = $('<thead></thead>').appendTo(this.$el);
        }


        this.$header.find('tr').each(function () {
            var column = [];

            $(this).find('th').each(function () {
                var $th = $(this), options =  $(this).data('options'),
                    options = (options && typeof options === 'string')? options.toObj():options;
                if (typeof $(this).data('field') !== 'undefined') {
                    $(this).data('field', $(this).data('field') + '');
                }
                column.push($.extend({}, {
                    th : $th,
                    title: $(this).html(),
                    'class': $(this).attr('class'),
                    titleTooltip: $(this).attr('title'),
                    rowspan: $(this).attr('rowspan') ? +$(this).attr('rowspan') : undefined,
                    colspan: $(this).attr('colspan') ? +$(this).attr('colspan') : undefined
                },options));
            });
            columns.push(column);
        });

        if(this.options.showCheckbox){
            columns[0].splice(0, 0,{field:'selectState',checkbox:true,visible:true});
        }else{
            columns[0].splice(0, 0,{field:'selectState',radio:true,visible:false});
        }

        if (!$.isArray(this.options.columns[0])) {
            this.options.columns = [this.options.columns];
        }
        this.options.columns = $.extend(true, [], columns, this.options.columns);

        this.columns = [];
        setFieldIndex(this.options.columns);
        $.each(this.options.columns, function (i, columns) {
            $.each(columns, function (j, column) {
                column = $.extend({}, BoltDataGrid.COLUMN_DEFAULTS, column);
                if (typeof column.fieldIndex !== 'undefined') {
                    that.columns[column.fieldIndex] = column;
                }
                that.options.columns[i][j] = column;
            });
        });
        //高度铺满：full-差距值
        if(options.height && /^full-\d+$/.test(options.height)){
            that.fullHeightGap = options.height.split('-')[1];
            options.height = $(window).height() - that.fullHeightGap;
        }
    }

    BoltDataGrid.prototype.initHeader = function () {
        var that = this,
            visibleColumns = {},
            html = [];

        this.header = {
            fields: [],
            styles: [],
            classes: [],
            formatters: [],
            events: [],
            sorters: [],
            sortNames: [],
            cellStyles: [],
            searchables: []
        };

        $.each(this.options.columns, function (i, columns) {
            html.push('<tr>');

            if (i === 0 && !that.options.cardView && that.options.detailView) {
                html.push(sprintf('<th class="detail" rowspan="%s"><div class="fht-cell"></div></th>',
                    that.options.columns.length));
            }

            $.each(columns, function (j, column) {
                var text = '',
                    halign = '', // header align style
                    align = '', // body align style
                    style = '',
                    class_ = sprintf(' class="%s"', column['class']),
                    order = that.options.sortOrder || column.order,
                    unitWidth = 'px',
                    width = column.width;

                if (column.width !== undefined && (!that.options.cardView)) {
                    if (typeof column.width === 'string') {
                        if (column.width.indexOf('%') !== -1) {
                            unitWidth = '%';
                        }
                    }
                }
                if (column.width && typeof column.width === 'string') {
                    width = column.width.replace('%', '').replace('px', '');
                }

                halign = sprintf('text-align: %s; ', column.halign ? column.halign : column.align);
                align = sprintf('text-align: %s; ', column.align);
                style = sprintf('vertical-align: %s; ', column.valign);
                style += sprintf('width: %s; ', (column.checkbox || column.radio) && !width ? '36px' : (width ? width + unitWidth : undefined));
                if(!column.visible){
                    style += ' display:none'
                }
                if (typeof column.fieldIndex !== 'undefined') {
                    that.header.fields[column.fieldIndex] = column.field;
                    that.header.styles[column.fieldIndex] = align + style;
                    that.header.classes[column.fieldIndex] = class_;
                    that.header.formatters[column.fieldIndex] = column.formatter;
                    that.header.events[column.fieldIndex] = column.events;
                    that.header.sorters[column.fieldIndex] = column.sorter;
                    that.header.sortNames[column.fieldIndex] = column.sortName;
                    that.header.cellStyles[column.fieldIndex] = column.cellStyle;
                    that.header.searchables[column.fieldIndex] = column.searchable;

                    if (that.options.cardView && (!column.cardVisible)) {
                        return;
                    }

                    visibleColumns[column.field] = column;
                }

                html.push('<th' + sprintf(' title="%s"', column.titleTooltip),
                    column.checkbox || column.radio ?
                        sprintf(' class="bs-checkbox %s"', column['class'] || '') :
                        class_,
                    sprintf(' style="%s"', halign + style),
                    sprintf(' rowspan="%s"', column.rowspan),
                    sprintf(' colspan="%s"', column.colspan),
                    sprintf(' data-field="%s"', column.field),
                    '>');

                html.push(sprintf('<div class="th-inner %s">', that.options.sortable && column.sortable ?
                    'sortable both' : ''));

                text = that.options.escape ? escapeHTML(column.title) : column.title;

                if (column.checkbox) {
                    if (!that.options.singleSelect && that.options.checkboxHeader) {
                        text = '<input name="btSelectAll" type="checkbox"/>';
                    }
                    that.header.selectStateField = column.field;
                }
                if (column.radio) {
                    text = '';
                    that.header.stateField = column.field;
                    that.options.singleSelect = true;
                }

                html.push(text);
                html.push('</div>');
                html.push('<div class="fht-cell"><div class="column-resizemark"></div></div>');
                html.push('</div>');
                html.push('</th>');
            });
            html.push('</tr>');
        });

        this.$header.html(html.join(''));
        this.$header.find('th[data-field]').each(function (i) {
            $(this).data(visibleColumns[$(this).data('field')]);
        });

        this.$container.off('click', '.th-inner').on('click', '.th-inner', function (event) {
            var target = $(this);

            if (that.options.detailView) {
                if (target.closest('.bootstrap-table')[0] !== that.$container[0])
                    return false;
            }

            if (that.options.sortable && target.parent().data().sortable) {
                that.onSort(event);
            }
        });

        this.$header.children().children().off('keypress').on('keypress', function (event) {
            if (that.options.sortable && $(this).data().sortable) {
                var code = event.keyCode || event.which;
                if (code == 13) { //Enter keycode
                    that.onSort(event);
                }
            }
        });

        $(window).off('resize.bootstrap-table');
        if (!this.options.showHeader) {
            this.$header.hide();
            this.$tableHeader.hide();
        } else {
            this.$header.show();
            this.$tableHeader.show();
            $(window).on('resize.bootstrap-table', $.proxy(this.resetSize, this));
        }
        this.$selectAll = this.$header.find('[name="btSelectAll"]');
        this.$selectAll.off('click').on('click', function () {
            var checked = $(this).prop('checked');
            that[checked ? 'checkAll' : 'uncheckAll']();
            that.updateSelected();
        });

    };

    BoltDataGrid.prototype.fitHeader = function () {
        var that = this,
            fixedBody,
            scrollWidth,
            focused,
            focusedTemp;

        if (that.$element.is(':hidden')) {
            that.timeoutId_ = setTimeout($.proxy(that.fitHeader, that), 100);
            return;
        }
        fixedBody = this.$tableBody.get(0);

        scrollWidth = fixedBody.scrollWidth > fixedBody.clientWidth &&
        fixedBody.scrollHeight > fixedBody.clientHeight + this.$header.outerHeight() ? getScrollBarWidth() : 0;

        this.$element.css('margin-top', -this.$header.outerHeight());

        focused = $(':focus');
        if (focused.length > 0) {
            var $th = focused.parents('th');
            if ($th.length > 0) {
                var dataField = $th.attr('data-field');
                if (dataField !== undefined) {
                    var $headerTh = this.$header.find("[data-field='" + dataField + "']");
                    if ($headerTh.length > 0) {
                        $headerTh.find(":input").addClass("focus-temp");
                    }
                }
            }
        }

        this.$header_ = this.$header.clone(true, true);
        this.$selectAll_ = this.$header_.find('[name="btSelectAll"]');
        this.$tableHeader.css({ 'margin-right': scrollWidth})
            .find('table').css('width', 0)
            .html('').attr('class', this.$element.attr('class'))
            .append(this.$header_);
        this.$tableBody .find('table').css('width', 0)

        focusedTemp = $('.focus-temp:visible:eq(0)');
        if (focusedTemp.length > 0) {
            focusedTemp.focus();
            this.$header.find('.focus-temp').removeClass('focus-temp');
        }

        // fix bug: $.data() is not working as expected after $.append()
        this.$header.find('th[data-field]').each(function (i) {
            that.$header_.find(sprintf('th[data-field="%s"]', $(this).data('field'))).data($(this).data());
        });

        var visibleFields = this.getVisibleFields(),
            $ths = this.$header_.find('th');

        this.$body.find('>tr:first-child:not(.no-records-found) > *').each(function (i) {
            var $this = $(this),
                index = i;

            if (that.options.detailView && !that.options.cardView) {
                if (i === 0) {
                    that.$header_.find('th.detail').find('.fht-cell').width($this.innerWidth());
                }
                index = i - 1;
            }

            var $th = that.$header_.find(sprintf('th[data-field="%s"]', visibleFields[index]));
            if ($th.length > 1) {
                $th = $($ths[$this[0].cellIndex]);
            }

            $th.find('.fht-cell').width($this.innerWidth());
        });
        // horizontal scroll event
        // TODO: it's probably better improving the layout than binding to scroll event
        this.$tableBody.off('scroll').on('scroll', function () {
            that.$tableHeader.scrollLeft($(this).scrollLeft());

            if (that.options.showFooter && !that.options.cardView) {
                that.$tableFooter.scrollLeft($(this).scrollLeft());
            }
        });
        that.trigger('post-header');
    };

    BoltDataGrid.prototype.resetHeader = function () {
        clearTimeout(this.timeoutId_);
        this.timeoutId_ = setTimeout($.proxy(this.fitHeader, this), this.$element.is(':hidden') ? 100 : 0);
    };

    BoltDataGrid.prototype.getVisibleFields = function () {
        var that = this,
            visibleFields = [];
        $.each(this.header.fields, function (j, field) {
            var column = that.columns[getFieldIndex(that.columns, field)];
            if (!column.visible) {
                return;
            }
            visibleFields.push(field);
        });
        return visibleFields;
    };

    BoltDataGrid.prototype.updateSelected = function () {
        var checkAll = this.$selectItem.filter(':enabled').length &&
            this.$selectItem.filter(':enabled').length ===
            this.$selectItem.filter(':enabled').filter(':checked').length;

        this.$selectAll.add(this.$selectAll_).prop('checked', checkAll);

        this.$selectItem.each(function () {
            $(this).closest('tr')[$(this).prop('checked') ? 'addClass' : 'removeClass']('selected');
        });
    };

    //设置列宽及高度
    BoltDataGrid.prototype.setArea = function(){
        var that = this,
            options = that.options
            ,colNums = 0 //列个数
            ,autoColNums = 0 //自动列宽的列个数
            ,autoWidth = 0 //自动列分配的宽度
            ,countWidth = 0 //所有列总宽度和
            ,cntrWidth = options.width || function(){ //获取容器宽度
                    //如果父元素宽度为0（一般为隐藏元素），则继续查找上层元素，直到找到真实宽度为止
                    var getWidth = function(parent){
                        var width, isNone;
                        parent = parent ||  that.$element.parent()
                        width = parent.width();
                        try {
                            isNone = parent.css('display') === 'none';
                        } catch(e){}
                        if(parent[0] && (!width || isNone)) return getWidth(parent.parent());
                        return width;
                    };
                    return getWidth();
                }();
        //统计列个数
        that.eachColumns(function(){
            colNums++;
        });

        //遍历所有列
        $.each(options.columns, function(i1, item1){
            $.each(item1, function(i2, item2){
                var width;
                if(!item2){
                    item1.splice(i2, 1);
                    return;
                }
                if(item2.radio){
                    return;
                }
                width = item2.width || 0;
                if(item2.colspan > 1) return;
                if(/\d+%$/.test(width)){
                    item2.width = width = Math.floor((parseFloat(width) / 100) * cntrWidth);
                } else if(!width){ //列宽未填写
                    item2.width = width = 0;
                    autoColNums++;
                }
                countWidth = countWidth + width;
            });
        });

        that.autoColNums = autoColNums; //记录自动列数

        //如果未填充满，则将剩余宽度平分。否则，给未设定宽度的列赋值一个默认宽
        (cntrWidth > countWidth && autoColNums) && (
            autoWidth = (cntrWidth - countWidth) / autoColNums
        );

        $.each(options.columns, function(i1, item1){
            $.each(item1, function(i2, item2){
                var minWidth = item2.minWidth || options.cellMinWidth;
                if(item2.colspan > 1) return;
                if(item2.width === 0){
                    item2.width = Math.floor(autoWidth >= minWidth ? autoWidth : minWidth); //不能低于设定的最小宽度
                }
            });
        });

        //高度铺满：full-差距值
        if(options.height && /^full-\d+$/.test(options.height)){
            that.fullHeightGap = options.height.split('-')[1];
            options.height = $(window).height() - that.fullHeightGap;
        }
    };

    //遍历表头
    BoltDataGrid.prototype.eachColumns = function(callback){
        var cols = $.extend(true, [], this.options.columns)
            ,arrs = [], index = 0;
        //重新整理表头结构
        $.each(cols, function(i1, item1){
            $.each(item1, function(i2, item2){
                //如果是组合列，则捕获对应的子列
                if(item2.colspan > 1){
                    var childIndex = 0;
                    index++
                    item2.CHILD_COLS = [];
                    $.each(cols[i1 + 1], function(i22, item22){
                        if(item22.PARENT_COL || childIndex == item2.colspan) return;
                        item22.PARENT_COL = index;
                        item2.CHILD_COLS.push(item22);
                        childIndex = childIndex + (item22.colspan > 1 ? item22.colspan : 1);
                    });
                }
                if(item2.PARENT_COL) return; //如果是子列，则不进行追加，因为已经存储在父列中
                arrs.push(item2)
            });
        });
        //重新遍历列，如果有子列，则进入递归
        var eachArrs = function(obj){
            $.each(obj || arrs, function(i, item){
                if(item.CHILD_COLS) return eachArrs(item.CHILD_COLS);
                callback(i, item);
            });
        };

        eachArrs();
    };

    BoltDataGrid.prototype.resetSize = function () {
        var that = this
            ,options = that.options
            ,height = options.height;
        if (options.showHeader && options.height) {
            this.fitHeader();
        }
        if(this.fullHeightGap){
            height = $(window).height() - that.fullHeightGap;
            if(height < 135) height = 135;
            options.height = height;
        }

        if(!height) return;
        this.resetView()
    };

    BoltDataGrid.prototype.initData = function (data, type) {
        if (this.options.treeView) {
            if (this.options.treeOptions.simpleData) {
                data = array2Json4Tree(this.options.treeOptions, data)
                data = json2Array4Tree(this.options.treeOptions.keys, data)
            } else {
                data = json2Array4Tree(this.options.treeOptions.keys, data)
            }
            console.log(data)
        }
        if (type === 'append') {
            this.data = this.data.concat(data);
        } else if (type === 'prepend') {
            this.data = [].concat(data).concat(this.data);
        } else {
            this.data = data || this.options.data;
        }

        // Fix #839 Records deleted when adding new row on filtered table
        if (type === 'append') {
            this.options.data = this.options.data.concat(data);
        } else if (type === 'prepend') {
            this.options.data = [].concat(data).concat(this.options.data);
        } else {
            this.options.data = this.data;
        }

        if (this.options.sidePagination === 'server') {
            return;
        }
        this.initSort();
    };

    BoltDataGrid.prototype.initSort = function () {
        var that = this,
            name = this.options.sortName,
            order = this.options.sortOrder === 'desc' ? -1 : 1,
            index = $.inArray(this.options.sortName, this.header.fields),
            timeoutId = 0;

        if (this.options.customSort !== $.noop) {
            this.options.customSort.apply(this, [this.options.sortName, this.options.sortOrder]);
            return;
        }

        if (index !== -1) {
            if (this.options.sortStable) {
                $.each(this.data, function (i, row) {
                    if (!row.hasOwnProperty('_position')) row._position = i;
                });
            }

            this.data.sort(function (a, b) {
                if (that.header.sortNames[index]) {
                    name = that.header.sortNames[index];
                }
                var aa = getItemField(a, name, that.options.escape),
                    bb = getItemField(b, name, that.options.escape),
                    value = calculateObjectValue(that.header, that.header.sorters[index], [aa, bb]);

                if (value !== undefined) {
                    return order * value;
                }

                // Fix #161: undefined or null string sort bug.
                if (aa === undefined || aa === null) {
                    aa = '';
                }
                if (bb === undefined || bb === null) {
                    bb = '';
                }

                if (that.options.sortStable && aa === bb) {
                    aa = a._position;
                    bb = b._position;
                }

                // IF both values are numeric, do a numeric comparison
                if ($.isNumeric(aa) && $.isNumeric(bb)) {
                    // Convert numerical values form string to float.
                    aa = parseFloat(aa);
                    bb = parseFloat(bb);
                    if (aa < bb) {
                        return order * -1;
                    }
                    return order;
                }

                if (aa === bb) {
                    return 0;
                }

                // If value is not a string, convert to string
                if (typeof aa !== 'string') {
                    aa = aa.toString();
                }

                if (aa.localeCompare(bb) === -1) {
                    return order * -1;
                }

                return order;
            });

            if (this.options.sortClass !== undefined) {
                clearTimeout(timeoutId);
                timeoutId = setTimeout(function () {
                    that.$el.removeClass(that.options.sortClass);
                    var index = that.$header.find(sprintf('[data-field="%s"]',
                            that.options.sortName).index() + 1);
                    that.$el.find(sprintf('tr td:nth-child(%s)', index))
                        .addClass(that.options.sortClass);
                }, 250);
            }
        }
    };

    BoltDataGrid.prototype.initPagination = function () {
        if (!this.options.paging) {
            this.$pagination.hide();
            return;
        } else {
            this.$pagination.show();
        }

        var that = this,
            html = [],
            $allSelected = false,
            i, from, to,
            $pageList,
            $first, $pre,
            $next, $last,
            $number,
            data = this.getData(),
            pageList = this.options.pageList;

        if (this.options.sidePagination !== 'server') {
            this.options.totalRows = data.length;
        }

        this.totalPages = 0;
        if (this.options.totalRows) {
            if (this.options.pageSize === this.options.formatAllRows()) {
                this.options.pageSize = this.options.totalRows;
                $allSelected = true;
            } else if (this.options.pageSize === this.options.totalRows) {
                // Fix #667 Table with pagination,
                // multiple pages and a search that matches to one page throws exception
                var pageLst = typeof this.options.pageList === 'string' ?
                    this.options.pageList.replace('[', '').replace(']', '')
                        .replace(/ /g, '').toLowerCase().split(',') : this.options.pageList;
                if ($.inArray(this.options.formatAllRows().toLowerCase(), pageLst)  > -1) {
                    $allSelected = true;
                }
            }

            this.totalPages = ~~((this.options.totalRows - 1) / this.options.pageSize) + 1;

            this.options.totalPages = this.totalPages;
        }
        if (this.totalPages > 0 && this.options.pageNumber > this.totalPages) {
            this.options.pageNumber = this.totalPages;
        }

        this.pageFrom = (this.options.pageNumber - 1) * this.options.pageSize + 1;
        this.pageTo = this.options.pageNumber * this.options.pageSize;
        if (this.pageTo > this.options.totalRows) {
            this.pageTo = this.options.totalRows;
        }
        html.push(
            '<div class="pull-' + this.options.paginationDetailHAlign + ' pagination-detail">',
            '<span class="pagination-info">',
            this.options.onlyInfoPagination ? this.options.formatDetailPagination(this.options.totalRows) :
            sprintf( Bolt.getRegional('datagrid.formatShowingRows'),this.pageFrom, this.pageTo, this.options.totalRows),
            '</span>');

        if (!this.options.onlyInfoPagination) {
            html.push('<span class="page-list">');

            var pageNumber = [
                sprintf('<span class="btn-group %s">',
                    this.options.paginationVAlign === 'top' || this.options.paginationVAlign === 'both' ?
                        'dropdown' : 'dropup'),
                '<button type="button" class="btn' +
                sprintf(' btn-%s', this.options.buttonsClass) +
                sprintf(' btn-%s', this.options.iconSize) +
                ' dropdown-toggle" data-toggle="dropdown">',
                '<span class="page-size">',
                $allSelected ? this.options.formatAllRows() : this.options.pageSize,
                '</span>',
                ' <span class="caret"></span>',
                '</button>',
                '<ul class="dropdown-menu" role="menu">'
            ];

            if (typeof this.options.pageList === 'string') {
                var list = this.options.pageList.replace('[', '').replace(']', '')
                    .replace(/ /g, '').split(',');

                pageList = [];
                $.each(list, function (i, value) {
                    pageList.push(value.toUpperCase() === that.options.formatAllRows().toUpperCase() ?
                        that.options.formatAllRows() : +value);
                });
            }

            $.each(pageList, function (i, page) {
                if (!that.options.smartDisplay || i === 0 || pageList[i - 1] < that.options.totalRows) {
                    var active;
                    if ($allSelected) {
                        active = page === that.options.formatAllRows() ? ' class="active"' : '';
                    } else {
                        active = page === that.options.pageSize ? ' class="active"' : '';
                    }
                    pageNumber.push(sprintf('<li role="menuitem"%s><a href="#">%s</a></li>', active, page));
                }
            });
            pageNumber.push('</ul></span>');

            html.push(sprintf( Bolt.getRegional('datagrid.formatRecordsPerPage'),pageNumber.join('')));
            html.push('</span>');

            html.push('</div>',
                '<div class="pull-' + this.options.paginationHAlign + ' pagination">',
                '<ul class="pagination' + sprintf(' pagination-%s', this.options.iconSize) + '">',
                '<li class="page-pre"><a href="#"><span class="glyphicon glyphicon-fast-backward" aria-hidden="true"></span></a></li>');

            if (this.totalPages < 5) {
                from = 1;
                to = this.totalPages;
            } else {
                from = this.options.pageNumber - 2;
                to = from + 4;
                if (from < 1) {
                    from = 1;
                    to = 5;
                }
                if (to > this.totalPages) {
                    to = this.totalPages;
                    from = to - 4;
                }
            }

            if (this.totalPages >= 6) {
                if (this.options.pageNumber >= 3) {
                    html.push('<li class="page-first' + (1 === this.options.pageNumber ? ' active' : '') + '">',
                        '<a href="#">', 1, '</a>',
                        '</li>');

                    from++;
                }

                if (this.options.pageNumber >= 4) {
                    if (this.options.pageNumber == 4 || this.totalPages == 6 || this.totalPages == 7) {
                        from--;
                    } else {
                        html.push('<li class="page-first-separator disabled">',
                            '<a href="#">...</a>',
                            '</li>');
                    }

                    to--;
                }
            }

            if (this.totalPages >= 7) {
                if (this.options.pageNumber >= (this.totalPages - 2)) {
                    from--;
                }
            }

            if (this.totalPages == 6) {
                if (this.options.pageNumber >= (this.totalPages - 2)) {
                    to++;
                }
            } else if (this.totalPages >= 7) {
                if (this.totalPages == 7 || this.options.pageNumber >= (this.totalPages - 3)) {
                    to++;
                }
            }

            for (i = from; i <= to; i++) {
                html.push('<li class="page-number' + (i === this.options.pageNumber ? ' active' : '') + '">',
                    '<a href="#">', i, '</a>',
                    '</li>');
            }

            if (this.totalPages >= 8) {
                if (this.options.pageNumber <= (this.totalPages - 4)) {
                    html.push('<li class="page-last-separator disabled">',
                        '<a href="#">...</a>',
                        '</li>');
                }
            }

            if (this.totalPages >= 6) {
                if (this.options.pageNumber <= (this.totalPages - 3)) {
                    html.push('<li class="page-last' + (this.totalPages === this.options.pageNumber ? ' active' : '') + '">',
                        '<a href="#">', this.totalPages, '</a>',
                        '</li>');
                }
            }

            html.push(
                '<li class="page-next"><a href="#"><span class="glyphicon glyphicon-fast-forward" aria-hidden="true"></span></a></li>',
                '</ul>',
                '</div>');
        }
        this.$pagination.html(html.join(''));

        if (!this.options.onlyInfoPagination) {
            $pageList = this.$pagination.find('.page-list a');
            $first = this.$pagination.find('.page-first');
            $pre = this.$pagination.find('.page-pre');
            $next = this.$pagination.find('.page-next');
            $last = this.$pagination.find('.page-last');
            $number = this.$pagination.find('.page-number');

            if (this.options.smartDisplay) {
                if (this.totalPages <= 1) {
                    this.$pagination.find('div.pagination').hide();
                }
                if (pageList.length < 2 || this.options.totalRows <= pageList[0]) {
                    this.$pagination.find('span.page-list').hide();
                }

                // when data is empty, hide the pagination
                this.$pagination[this.getData().length ? 'show' : 'hide']();
            }

            if (!this.options.paginationLoop) {
                if (this.options.pageNumber === 1) {
                    $pre.addClass('disabled');
                }
                if (this.options.pageNumber === this.totalPages) {
                    $next.addClass('disabled');
                }
            }

            if ($allSelected) {
                this.options.pageSize = this.options.formatAllRows();
            }
            $pageList.off('click').on('click', $.proxy(this.onPageListChange, this));
            $first.off('click').on('click', $.proxy(this.onPageFirst, this));
            $pre.off('click').on('click', $.proxy(this.onPagePre, this));
            $next.off('click').on('click', $.proxy(this.onPageNext, this));
            $last.off('click').on('click', $.proxy(this.onPageLast, this));
            $number.off('click').on('click', $.proxy(this.onPageNumber, this));
        }
    };

    BoltDataGrid.prototype.initBody = function (fixedScroll) {
        var that = this,
            html = [],
            data = this.getData();

        this.trigger('pre-body', data);

        this.$body = this.$element.find('>tbody');
        if (!this.$body.length) {
            this.$body = $('<tbody></tbody>').appendTo(this.$element);
        }

        //Fix #389 Bootstrap-table-flatJSON is not working

        if (!this.options.pagination || this.options.sidePagination === 'server') {
            this.pageFrom = 1;
            this.pageTo = data.length;
        }


        var trFragments = $(document.createDocumentFragment());
        var hasTr;

        for (var i = this.pageFrom - 1; i < this.pageTo; i++) {
            var item = data[i];
            var tr = this.initRow(item, i, data, trFragments);
            hasTr = hasTr || !!tr;
            if (tr&&tr!==true) {
                trFragments.append(tr);
            }
        }

        // show no records
        if (!hasTr) {
            trFragments.append('<tr class="no-records-found">' +
                sprintf('<td colspan="%s">%s</td>',
                    this.$header.find('th').length,
                    Bolt.getRegional('datagrid.noData')) +
                '</tr>');
        }

        this.$body.html(trFragments);

        this.$body.find('> tr[data-index] > td [data-toggle="rowToolbar"]').each(function(e,element){
            var $this = $(element),$tr = $this.closest('tr');
            that.initToolEvent($this,$tr)
        });

        // click to select by column
        this.$body.find('> tr[data-index] > td').off('click dblclick').on('click dblclick', function (e) {
            var $td = $(this),
                $tr = $td.parent(),
                item = that.data[$tr.data('index')],
                index = $td[0].cellIndex,
                fields = that.getVisibleFields(),
                field = fields[that.options.detailView && !that.options.cardView ? index - 1 : index],
                column = that.columns[getFieldIndex(that.columns, field)],
                value = getItemField(item, field, that.options.escape);

            if ($td.find('.detail-icon').length) {
                return;
            }

            that.trigger(e.type === 'click' ? 'click-cell' : 'dbl-click-cell', field, value, item, $td);
            that.trigger(e.type === 'click' ? 'click-row' : 'dbl-click-row', item, $tr, field);

            // if click to select - then trigger the checkbox/radio click
            if (e.type === 'click') {
                var $selectItem = $tr.find(sprintf('[name="%s"]', that.options.selectItemName));
                if ($selectItem.length) {
                    $selectItem[0].click(); // #144: .trigger('click') bug
                }
            }

        });

        this.$body.find('> tr[data-index] > td > .detail-icon').off('click').on('click', function () {
            var $this = $(this),
                $tr = $this.parent().parent(),
                index = $tr.data('index'),
                row = data[index]; // Fix #980 Detail view, when searching, returns wrong row

            // remove and update
            if ($tr.next().is('tr.detail-view')) {
                $this.find('i').attr('class', sprintf('%s %s', that.options.iconsPrefix, that.options.icons.detailOpen));
                that.trigger('collapse-row', index, row);
                $tr.next().remove();
            } else {
                $this.find('i').attr('class', sprintf('%s %s', that.options.iconsPrefix, that.options.icons.detailClose));
                $tr.after(sprintf('<tr class="detail-view"><td colspan="%s"></td></tr>', $tr.find('td').length));
                var $element = $tr.next().find('td');
                var content = calculateObjectValue(that.options, that.options.detailFormatter, [index, row, $element], '');
                if($element.length === 1) {
                    $element.append(content);
                }
                that.trigger('expand-row', index, row, $element);
            }
            that.resetView();
            return false;
        });
        this.$body.find('> tr[data-index] > td > .tree-icon').off('click').on('click', function (e) {
            e.stopPropagation()
            e.preventDefault()
            var $this = $(this),
                $tr = $this.parent().parent(),
                index = $tr.data('index'),
                row = data[index] ,
                isExpand = !$this.hasClass('collapsed'),
                childLen = row['childLen'], level = row['level'];
            if (childLen) {
                $tr.data('isExpand', !isExpand)
                    .nextAll('[datagrid-tree-level-'+ (level + 1)+']').slice(0, childLen)
                    .toggleClass('datagrid-tree-tr collapsed', isExpand);
                if(isExpand){
                   that.trigger('expand-row', index, row);
                }
                $this.toggleClass('collapsed', isExpand).find('> i')
                    .attr('class', (isExpand ? that.options.collapseIcon : that.options.expandIcon))
            }


        });

        this.$selectItem = this.$body.find(sprintf('[name="%s"]', this.options.selectItemName));
        this.$selectItem.off('click').on('click', function (event) {
            event.stopImmediatePropagation();

            var $this = $(this),
                checked = $this.prop('checked'),
                row = that.data[$this.data('index')];

            if (that.options.maintainSelected && $(this).is(':radio')) {
                $.each(that.options.data, function (i, row) {
                    row[that.header.stateField] = false;
                });
            }

            row[that.header.stateField] = checked;

            if (that.options.singleSelect) {
                that.$selectItem.not(this).each(function () {
                    that.data[$(this).data('index')][that.header.stateField] = false;
                });
                that.$selectItem.filter(':checked').not(this).prop('checked', false);
            }

            that.updateSelected();
            that.trigger(checked ? 'check' : 'uncheck', row, $this);
        });

        $.each(this.header.events, function (i, events) {
            if (!events) {
                return;
            }
            // fix bug, if events is defined with namespace
            if (typeof events === 'string') {
                events = calculateObjectValue(null, events);
            }

            var field = that.header.fields[i],
                fieldIndex = $.inArray(field, that.getVisibleFields());

            if (that.options.detailView && !that.options.cardView) {
                fieldIndex += 1;
            }

            for (var key in events) {
                that.$body.find('>tr:not(.no-records-found)').each(function () {
                    var $tr = $(this),
                        $td = $tr.find(that.options.cardView ? '.card-view' : 'td').eq(fieldIndex),
                        index = key.indexOf(' '),
                        name = key.substring(0, index),
                        el = key.substring(index + 1),
                        func = events[key];

                    $td.find(el).off(name).on(name, function (e) {
                        var index = $tr.data('index'),
                            row = that.data[index],
                            value = row[field];

                        func.apply(this, [e, value, row, index]);
                    });
                });
            }
        });

        this.updateSelected();
        this.resetView();
        this.initTableDnDEvent();
        this.trigger('post-body', data);
    };

    BoltDataGrid.prototype.resetView = function (params) {
        var padding = 0;

        if (params && params.height) {
            this.options.height = params.height;
        }
        if(params && params.space){
            this.options.height = ( this.options.height + params.space);
        }
        this.$selectAll.prop('checked', this.$selectItem.length > 0 &&
            this.$selectItem.length === this.$selectItem.filter(':checked').length);

        if (this.options.height) {
            var paginationHeight = this.$pagination.outerHeight(true),
                height = this.options.height  - paginationHeight;

            this.$tableContainer.css('height', height + 'px');
        }

        if (this.options.cardView) {
            // remove the element css
            this.$el.css('margin-top', '0');
            this.$tableContainer.css('padding-bottom', '0');
            this.$tableFooter.hide();
            return;
        }

        if (this.options.showHeader && this.options.height) {
            this.$tableHeader.show();
            this.resetHeader();
            padding += this.$header.outerHeight();
        } else {
            this.$tableHeader.hide();
            this.trigger('post-header');
        }

        if (this.options.showFooter) {
            this.resetFooter();
            if (this.options.height) {
                padding += this.$tableFooter.outerHeight() + 1;
            }
        }

        // Assign the correct sortable arrow
        //this.getCaret();
        this.$tableContainer.css('padding-bottom', padding + 'px');
        this.trigger('reset-view');
    };

    BoltDataGrid.prototype.onPageNumber = function (event) {
        if (this.options.pageNumber === +$(event.currentTarget).text()) {
            return;
        }
        this.options.pageNumber = +$(event.currentTarget).text();
        this.updatePagination(event);
        return false;
    };

    BoltDataGrid.prototype.initRow = function(item, i, data, parentDom) {
        var that=this,
            key,
            html = [],
            style = {},
            csses = [],
            hasTree=false,
            data_ = '',
            attributes = {},
            htmlAttributes = [];

        if ($.inArray(item, this.hiddenRows) > -1) {
            return;
        }

        style = calculateObjectValue(this.options, this.options.rowStyle, [item, i], style);

        if (style && style.css) {
            for (key in style.css) {
                csses.push(key + ': ' + style.css[key]);
            }
        }

        attributes = calculateObjectValue(this.options,
            this.options.rowAttributes, [item, i], attributes);

        if (attributes) {
            for (key in attributes) {
                htmlAttributes.push(sprintf('%s="%s"', key, escapeHTML(attributes[key])));
            }
        }

        if (item._data && !$.isEmptyObject(item._data)) {
            $.each(item._data, function(k, v) {
                // ignore data-index
                if (k === 'index') {
                    return;
                }
                data_ += sprintf(' data-%s="%s"', k, v);
            });
        }
        if(that.options.treeView){
            data_ += sprintf(' datagrid-tree-level-%s',item.level)
        }
        html.push('<tr',
            sprintf(' %s', htmlAttributes.join(' ')),
            sprintf(' id="%s"', $.isArray(item) ? undefined : item._id),
            sprintf(' class="%s"', style.classes || ($.isArray(item) ? undefined : item._class)),
            sprintf(' data-index="%s"', i),
            sprintf(' data-uniqueid="%s"', item[this.options.uniqueId]),
            sprintf('%s', data_),
            '>'
        );

        if (this.options.cardView) {
            html.push(sprintf('<td colspan="%s"><div class="card-views">', this.header.fields.length));
        }

        if (!this.options.cardView && this.options.detailView) {
            html.push('<td>',
                '<a class="detail-icon" href="#">',
                sprintf('<i class="%s %s"></i>', this.options.iconsPrefix, this.options.icons.detailOpen),
                '</a>',
                '</td>');
        }

        $.each(this.header.fields, function(j, field) {
            var text = '',
                value_ = getItemField(item, field, that.options.escape),
                value = '',
                type = '',
                cellStyle = {},
                id_ = '',
                class_ = that.header.classes[j],
                data_ = '',
                rowspan_ = '',
                colspan_ = '',
                title_ = '',
                column = that.columns[j];

            if (that.fromHtml && typeof value_ === 'undefined') {
                return;
            }



            if (that.options.cardView && (!column.cardVisible)) {
                return;
            }

            if (column.escape) {
                value_ = escapeHTML(value_);
            }

            style = sprintf('style="vertical-align:middle;%s"', csses.concat(that.header.styles[j]).join('; '));

            // handle td's id and class
            if (item['_' + field + '_id']) {
                id_ = sprintf(' id="%s"', item['_' + field + '_id']);
            }
            if (item['_' + field + '_class']) {
                class_ = sprintf(' class="%s"', item['_' + field + '_class']);
            }
            if (item['_' + field + '_rowspan']) {
                rowspan_ = sprintf(' rowspan="%s"', item['_' + field + '_rowspan']);
            }
            if (item['_' + field + '_colspan']) {
                colspan_ = sprintf(' colspan="%s"', item['_' + field + '_colspan']);
            }
            if (item['_' + field + '_title']) {
                title_ = sprintf(' title="%s"', item['_' + field + '_title']);
            }
            cellStyle = calculateObjectValue(that.header,
                that.header.cellStyles[j], [value_, item, i, field], cellStyle);
            if (cellStyle.classes) {
                class_ = sprintf(' class="%s"', cellStyle.classes);
            }
            if (cellStyle.css) {
                var csses_ = [];
                for (var key in cellStyle.css) {
                    csses_.push(key + ': ' + cellStyle.css[key]);
                }
                style = sprintf('style="%s"', csses_.concat(that.header.styles[j]).join('; '));
            }

            value = calculateObjectValue(column, that.header.formatters[j], [value_, item, i], value_);

            if (item['_' + field + '_data'] && !$.isEmptyObject(item['_' + field + '_data'])) {
                $.each(item['_' + field + '_data'], function(k, v) {
                    // ignore data-index
                    if (k === 'index') {
                        return;
                    }
                    data_ += sprintf(' data-%s="%s"', k, v);
                });
            }

            if (column.checkbox || column.radio) {
                type = column.checkbox ? 'checkbox' : type;
                type = column.radio ? 'radio' : type;

                text = [sprintf(that.options.cardView ?
                    '<div class="card-view %s">' : '<td class="bs-checkbox %s"'+(column.radio? 'style= "display:none"':'')+'>', column['class'] || ''),
                    '<input' +
                    sprintf(' data-index="%s"', i) +
                    sprintf(' name="%s"', that.options.selectItemName) +
                    sprintf(' type="%s"', type) +
                    sprintf(' value="%s"', item[that.options.idField]) +
                    sprintf(' checked="%s"', value === true ||
                    (value_ || value && value.checked) ? 'checked' : undefined) +
                    sprintf(' disabled="%s"', !column.checkboxEnabled ||
                    (value && value.disabled) ? 'disabled' : undefined) +
                    ' />',
                    that.header.formatters[j] && typeof value === 'string' ? value : '',
                    that.options.cardView ? '</div>' : '</td>'
                ].join('');

                item[that.header.stateField] = value === true || (value && value.checked);
            } else {
                value = typeof value === 'undefined' || value === null ?
                    that.options.undefinedText : value;
                var indent, treePlaceholder,icon,keys = that.options.treeOptions.keys
                if (that.options.treeView && !hasTree && column.visible ) {
                    var indent = item.level  == 0 ? '' : sprintf('<span style="margin-left: %spx;"></span>', item.level * 15);
                    treePlaceholder = sprintf('<span class="tree-icon " style="cursor: pointer; margin: 0px 5px;"><i class="%s"></i> </span>',
                        item[keys.isParent] ? that.options.expandIcon : that.options.collapseIcon);
                    style = "text-align: left";
                    hasTree = true;
                    if(item.icon){
                        icon =  '<span class="datagrid-tree-branch"><i class="zmdi '+item.icon+'"></i></span>'
                    }
                }
                text = that.options.cardView ? ['<div class="card-view">',
                        that.options.showHeader ? sprintf('<span class="title" %s>%s</span>', style,
                                getPropertyFromOther(that.columns, 'field', 'title', field)) : '',
                        sprintf('<span class="value">%s</span>', value),
                        '</div>'
                    ].join('') : [sprintf('<td%s %s %s %s %s %s>', id_, class_, style, data_, rowspan_, title_),
                        indent,
                        treePlaceholder,
                        icon,
                        value,
                        '</td>'
                    ].join('');

                // Hide empty data on Card view when smartDisplay is set to true.
                if (that.options.cardView && that.options.smartDisplay && value === '') {
                    // Should set a placeholder for event binding correct fieldIndex
                    text = '<div class="card-view"></div>';
                }
            }

            html.push(text);
        });

        if (this.options.cardView) {
            html.push('</div></td>');
        }

        html.push('</tr>');

        return html.join(' ');
    };

    BoltDataGrid.prototype.initToolEvent = function(element,row){
        var that = this;
        element.on('click','a',function(event){

            var link = event.currentTarget,
                $link = $(link),
                href = $link.attr('href'),
                data = $link.data(),
                options = data.options,
                selected = [],dataUrl=null;
            if (!data.title) data.title = $link.text();
            if (href) data.url = href;
            if (options) {
                if (typeof options === 'string') options = options.toObj()
                if (typeof options === 'object')
                    $.extend(data, options)
            }
            if(row){
                var item = that.data[row.data('index')];
                selected.push(item) ;
            }else{
                selected = that.getSelections();
            }
            if(selected){
                var render =  template.compile(data.url);
                data.url = render(selected[0])
            }
            data['onClose']  = function(){
                that.refresh();
            }
            if(data.toggle == 'add'){
                event.preventDefault()
                $link.dialog(data)
            }else if(data.toggle == 'edit'){
                event.preventDefault()
                if (!selected.length) {
                    Bolt.notify('info', Bolt.getRegional('datagrid.selectMsg'))
                    return false;
                }
                $link.dialog(data)

            }else if(data.toggle == 'del'){
                event.preventDefault()
                var param = [] ;
                if (!selected) {
                    Bolt.notify('info', Bolt.getRegional('datagrid.selectMsg'))
                    return false;
                }
                for(var i=0;i<selected.length;i++) {
                    param.push(selected[i]['id']);
                }

                data['data'] = {'id':param.toString()}

                if(!data.confirmMsg){
                    data.confirmMsg="确定要删除此项吗?";
                }
                if(!data.type){
                    data.type = 'GET'
                }
                data['okCallback']  = function(){
                    that.refresh();
                }
                Bolt.ajax('doajax',data);

            }else if(data.toggle == 'navtab'){
                event.preventDefault()
                data.isOpen = true;
                data.id = data.title
                $link.navtab(data)

            }else if(data.toggle == 'view'){
                event.preventDefault()
                if (!selected.length) {
                    Bolt.notify('info', Bolt.getRegional('datagrid.selectMsg'))
                    return false;
                }
                $link.dialog(data)
            }else if(data.toggle == 'doajax'){
                event.preventDefault()
                data['okCallback']  = function(){
                    that.refresh();
                }
                Bolt.ajax('doajax',data);
            }else if(data.toggle == 'fun'){
                event.preventDefault()
                if(!Bolt.isEmpty(data.fun)){
                    if (!selected.length) {
                        Bolt.notify('info', Bolt.getRegional('datagrid.selectMsg'))
                        return false;
                    }
                    var clickFunc = data.fun.toFunc();
                    clickFunc.apply(that,selected)
                }


            }

        })
    }


    BoltDataGrid.prototype.getData = function (useCurrentPage) {
        return  (useCurrentPage ? this.data.slice(this.pageFrom - 1, this.pageTo) : this.data) ;
    };

    BoltDataGrid.prototype.updatePagination = function (event) {
        // Fix #171: IE disabled button can be clicked bug.
        if (event && $(event.currentTarget).hasClass('disabled')) {
            return;
        }

        if (!this.options.maintainSelected) {
            this.resetRows();
        }

        this.initPagination();
        if (this.options.sidePagination === 'server') {
            this.initServer();
        } else {
            this.initBody();
        }

        this.trigger('page-change', this.options.pageNumber, this.options.pageSize);
    };

    BoltDataGrid.prototype.onPageListChange = function (event) {
        var $this = $(event.currentTarget);

        $this.parent().addClass('active').siblings().removeClass('active');
        this.options.pageSize = $this.text().toUpperCase() === this.options.formatAllRows().toUpperCase() ?
            this.options.formatAllRows() : +$this.text();
        this.$toolbar.find('.page-size').text(this.options.pageSize);

        this.updatePagination(event);
        return false;
    };

    BoltDataGrid.prototype.onPageFirst = function (event) {
        this.options.pageNumber = 1;
        this.updatePagination(event);
        return false;
    };

    BoltDataGrid.prototype.onPagePre = function (event) {
        if ((this.options.pageNumber - 1) === 0) {
            this.options.pageNumber = this.options.totalPages;
        } else {
            this.options.pageNumber--;
        }
        this.updatePagination(event);
        return false;
    };

    BoltDataGrid.prototype.onPageNext = function (event) {
        if ((this.options.pageNumber + 1) > this.options.totalPages) {
            this.options.pageNumber = 1;
        } else {
            this.options.pageNumber++;
        }
        this.updatePagination(event);
        return false;
    };

    BoltDataGrid.prototype.onPageLast = function (event) {
        this.options.pageNumber = this.totalPages;
        this.updatePagination(event);
        return false;
    };

    BoltDataGrid.prototype.onPageNumber = function (event) {
        if (this.options.pageNumber === +$(event.currentTarget).text()) {
            return;
        }
        this.options.pageNumber = +$(event.currentTarget).text();
        this.updatePagination(event);
        return false;
    };

    BoltDataGrid.prototype.resetRows = function () {
        var that = this;

        $.each(this.data, function (i, row) {
            that.$selectAll.prop('checked', false);
            that.$selectItem.prop('checked', false);
            if (that.header.stateField) {
                row[that.header.stateField] = false;
            }
        });
        this.initHiddenRows();
    };

    BoltDataGrid.prototype.initHiddenRows = function () {
        this.hiddenRows = [];
    };

    BoltDataGrid.prototype.showRow = function (params) {
        this.toggleRow(params, true);
    };

    BoltDataGrid.prototype.hideRow = function (params) {
        this.toggleRow(params, false);
    };

    BoltDataGrid.prototype.toggleRow = function (params, visible) {
        var row, index;

        if (params.hasOwnProperty('index')) {
            row = this.getData()[params.index];
        } else if (params.hasOwnProperty('uniqueId')) {
            row = this.getRowByUniqueId(params.uniqueId);
        }

        if (!row) {
            return;
        }

        index = $.inArray(row, this.hiddenRows);

        if (!visible && index === -1) {
            this.hiddenRows.push(row);
        } else if (visible && index > -1) {
            this.hiddenRows.splice(index, 1);
        }
        this.initBody(true);
    };

    BoltDataGrid.prototype.getHiddenRows = function (show) {
        var that = this,
            data = this.getData(),
            rows = [];

        $.each(data, function (i, row) {
            if ($.inArray(row, that.hiddenRows) > -1) {
                rows.push(row);
            }
        });
        this.hiddenRows = rows;
        return rows;
    };

    BoltDataGrid.prototype.getOptions = function () {
        return this.options;
    };

    BoltDataGrid.prototype.getSelections = function () {
        var that = this;

        return $.grep(this.options.data, function (row) {
            // fix #2424: from html with checkbox
            return row[that.header.stateField] === true;
        });
    };

    BoltDataGrid.prototype.getAllSelections = function () {
        var that = this;

        return $.grep(this.options.data, function (row) {
            return row[that.header.stateField];
        });
    };

    BoltDataGrid.prototype.checkAll = function () {
        this.checkAll_(true);
    };

    BoltDataGrid.prototype.uncheckAll = function () {
        this.checkAll_(false);
    };

    BoltDataGrid.prototype.checkAll_ = function (checked) {
        var rows;
        if (!checked) {
            rows = this.getSelections();
        }
        this.$selectAll.add(this.$selectAll_).prop('checked', checked);
        this.$selectItem.filter(':enabled').prop('checked', checked);
        this.updateRows();
        if (checked) {
            rows = this.getSelections();
        }
        this.trigger(checked ? 'check-all' : 'uncheck-all', rows);
    };

    BoltDataGrid.prototype.trigger = function (name) {
        var args = Array.prototype.slice.call(arguments, 1);
        name += '.bs.table';
        this.options[BoltDataGrid.EVENTS[name]].apply(this.options, args);
        this.$element.trigger($.Event(name), args);
        this.options.onAll(name, args);
        this.$element.trigger($.Event('all.bs.table'), [name, args]);
    };


    BoltDataGrid.prototype.initServer = function (query, dataUrl) {
        var that = this,
            data = {},
            params = {
                sortName: this.options.sortName,
                sortOrder: this.options.sortOrder
            },
            request;
        if (!(dataUrl || this.options.dataUrl)) {
            return;
        }
        if (that.options.paging) {
            params.pageNumber = (this.options.pageNumber-1);
            params.pageSize =  this.options.pageSize;
        }
        that.$tableLoading && that.$tableLoading.show().trigger('Bolt.ajaxStart');
        data = $.extend(params , this.options.queryParams||{});
        $.extend(data, query || {});

        request = $.extend({}, calculateObjectValue(null, this.options.ajaxOptions), {
            type: this.options.method,
            url:  dataUrl || this.options.dataUrl,
            data: data,
            cache: this.options.cache,
            dataType: this.options.dataType,
            okCallback: function (res) {
                res = calculateObjectValue(that.options, that.options.responseHandler, [res], res);
                that.load(res);
                that.trigger('load-success', res);
                that.$tableLoading && that.$tableLoading.trigger('Bolt.ajaxStop').hide()
            },
            errCallback: function (res) {
                that.trigger('load-error', res.status, res);
                if (res && res[Bolt.keys.statusCode]) {
                    Bolt.notify('error', res[Bolt.keys.message] || Bolt.getRegional('datagrid.errorData'))
                } else {
                    Bolt.notify('warn', Bolt.getRegional('datagrid.errorData'))
                }
            }
        });

        Bolt.ajax('doajax', request);

    }

    BoltDataGrid.prototype.refresh = function (params,clearOldPostData) {
        var that = this, options = that.options;
        if (params && params.dataUrl) {
            options.dataUrl = params.dataUrl;
        }
        if (params && params.pageNumber) {
            options.pageNumber = params.pageNumber;
        }
        if (params && params.pageSize) {
            options.pageSize = params.pageSize;
        }
        if (params && typeof params === 'object') {
            if (clearOldPostData || !options.queryParams)
                options.queryParams = {}
            $.extend(options.queryParams, params)
        }
        this.initServer(params, params && params.dataUrl);
        this.trigger('refresh', params);
    };

    BoltDataGrid.prototype.load = function (data) {
        if (this.options.sidePagination === 'server') {
            this.options.totalRows = data[this.options.totalField];
            data = data[this.options.dataField];
        } else if (!$.isArray(data)) { // support fixedScroll
            data = data.data;
        }

        this.initData(data);
        this.initPagination();
        this.initBody();
    };

    function Plugin (option) {
        var value,
            args = Array.prototype.slice.call(arguments, 1);
        this.each(function () {
            var $this = $(this),
                data = $this.data('bolt.table'),
                optionsData = $this.data().options,
                optionsData =  (optionsData && typeof optionsData === 'string') ?optionsData.toObj():optionsData,
                options = $.extend({}, BoltDataGrid.DEFAULTS, optionsData,  typeof option === 'object' && option);

            if (typeof option === 'string') {

                if (!data) {
                    return;
                }
                value = data[option].apply(data, args);
                if (option === 'destroy') {
                    $this.removeData('bolt.table');
                }
            }

            if (!data) {
                $this.data('bolt.table', (new BoltDataGrid(this, options)));
            }
        });
        return typeof value === 'undefined' ? this : value;
    };
    $.fn.boltDataGrid = Plugin
    $.fn.boltDataGrid.Constructor = BoltDataGrid;
    $.fn.boltDataGrid.defaults = BoltDataGrid.DEFAULTS;

    $(document).on(Bolt.eventType.initUI, function(e) {
        $(e.target).find('table[data-toggle="boltDataGrid"]').each(function() {
            var $this = $(this), options = $this.data()
            if (!$this.length) return
            if (options.options && typeof options.options === 'string') options.options = options.options.toObj()
            $.extend(options, typeof options.options === 'object' && options.options)
            Plugin.call($this, options)
        })
    })
    Bolt.addComponent({
        name: "boltDataGrid",
        plugin: Plugin,
        runPoint: Bolt.eventType.afterAjaxPageShow,
        expr: "table[data-toggle='boltDataGrid']"
    })

}(jQuery);

